"""
部署服务 - 处理发布和回滚的核心业务逻辑
"""
import asyncio
import logging
from typing import Dict, List, Optional, Any
from datetime import datetime
from sqlalchemy.orm import Session
from sqlalchemy import and_, desc

from app.models.release import (
    Release, ReleaseStep, HealthCheck, RollbackRecord,
    ReleaseStatus, DeploymentStrategy, Environment
)
from app.services.health_check_service import HealthCheckService
from app.services.notification_service import NotificationService
from app.utils.deployment_strategies import (
    BlueGreenDeployment, CanaryDeployment, RollingDeployment
)
from app.utils.backup_service import BackupService

logger = logging.getLogger(__name__)


class DeploymentService:
    """部署服务类"""
    
    def __init__(self, db: Session):
        self.db = db
        self.health_check_service = HealthCheckService(db)
        self.notification_service = NotificationService()
        self.backup_service = BackupService()
    
    async def create_release(self, release_data: Dict[str, Any]) -> Release:
        """创建发布记录"""
        try:
            # 验证版本号格式
            version = release_data.get("version")
            if not self._validate_version(version):
                raise ValueError(f"无效的版本号格式: {version}")
            
            # 检查版本是否已存在
            existing_release = self.db.query(Release).filter(
                and_(
                    Release.version == version,
                    Release.environment == release_data.get("environment")
                )
            ).first()
            
            if existing_release:
                raise ValueError(f"版本 {version} 在 {release_data.get('environment')} 环境中已存在")
            
            # 创建发布记录
            release = Release(
                version=version,
                description=release_data.get("description", ""),
                environment=release_data.get("environment"),
                strategy=release_data.get("strategy"),
                config=release_data.get("config", {}),
                artifacts=release_data.get("artifacts", {}),
                deployed_by=release_data.get("deployed_by", "system")
            )
            
            self.db.add(release)
            self.db.commit()
            self.db.refresh(release)
            
            # 创建发布步骤
            await self._create_deployment_steps(release)
            
            logger.info(f"创建发布记录成功: {release.id} - {version}")
            return release
            
        except Exception as e:
            self.db.rollback()
            logger.error(f"创建发布记录失败: {e}")
            raise
    
    async def deploy_release(self, release_id: int, user: str = "system") -> Dict[str, Any]:
        """执行发布"""
        try:
            release = self.db.query(Release).filter(Release.id == release_id).first()
            if not release:
                raise ValueError(f"发布记录不存在: {release_id}")
            
            if release.status != ReleaseStatus.PENDING:
                raise ValueError(f"发布状态不允许执行: {release.status}")
            
            # 更新发布状态
            release.status = ReleaseStatus.IN_PROGRESS
            release.deployed_at = datetime.utcnow()
            release.deployed_by = user
            
            self.db.commit()
            
            # 发送通知
            await self.notification_service.send_deployment_start_notification(release)
            
            # 执行部署策略
            deployment_result = await self._execute_deployment_strategy(release)
            
            if deployment_result["success"]:
                release.status = ReleaseStatus.SUCCESS
                release.completed_at = datetime.utcnow()
                await self.notification_service.send_deployment_success_notification(release)
            else:
                release.status = ReleaseStatus.FAILED
                await self.notification_service.send_deployment_failure_notification(release)
                
                # 自动回滚检查
                if self._should_auto_rollback(release):
                    await self.rollback_release(release_id, "自动回滚", user)
            
            self.db.commit()
            
            logger.info(f"发布执行完成: {release_id} - {deployment_result['success']}")
            return deployment_result
            
        except Exception as e:
            self.db.rollback()
            logger.error(f"发布执行失败: {e}")
            raise
    
    async def rollback_release(self, release_id: int, reason: str, user: str = "system") -> Dict[str, Any]:
        """执行回滚"""
        try:
            release = self.db.query(Release).filter(Release.id == release_id).first()
            if not release:
                raise ValueError(f"发布记录不存在: {release_id}")
            
            # 查找上一个稳定版本
            previous_release = self._get_previous_stable_release(release)
            if not previous_release:
                raise ValueError("没有找到可回滚的稳定版本")
            
            # 创建回滚记录
            rollback_record = RollbackRecord(
                from_version=release.version,
                to_version=previous_release.version,
                environment=release.environment,
                reason=reason,
                initiated_by=user
            )
            
            self.db.add(rollback_record)
            self.db.commit()
            
            # 执行回滚
            rollback_result = await self._execute_rollback(release, previous_release)
            
            if rollback_result["success"]:
                # 更新发布状态
                release.status = ReleaseStatus.ROLLED_BACK
                release.rollback_reason = reason
                release.rollback_by = user
                release.rollback_at = datetime.utcnow()
                release.rollback_to_version = previous_release.version
                
                # 更新回滚记录
                rollback_record.status = ReleaseStatus.SUCCESS
                rollback_record.completed_at = datetime.utcnow()
                
                await self.notification_service.send_rollback_success_notification(
                    release, previous_release
                )
            else:
                rollback_record.status = ReleaseStatus.FAILED
                await self.notification_service.send_rollback_failure_notification(
                    release, previous_release, rollback_result["error"]
                )
            
            self.db.commit()
            
            logger.info(f"回滚执行完成: {release_id} -> {previous_release.version}")
            return rollback_result
            
        except Exception as e:
            self.db.rollback()
            logger.error(f"回滚执行失败: {e}")
            raise
    
    async def get_release_status(self, release_id: int) -> Dict[str, Any]:
        """获取发布状态"""
        release = self.db.query(Release).filter(Release.id == release_id).first()
        if not release:
            raise ValueError(f"发布记录不存在: {release_id}")
        
        # 获取步骤状态
        steps = self.db.query(ReleaseStep).filter(
            ReleaseStep.release_id == release_id
        ).order_by(ReleaseStep.step_order).all()
        
        # 获取健康检查结果
        health_checks = self.db.query(HealthCheck).filter(
            HealthCheck.release_id == release_id
        ).order_by(HealthCheck.checked_at.desc()).limit(10).all()
        
        return {
            "release": {
                "id": release.id,
                "version": release.version,
                "status": release.status,
                "environment": release.environment,
                "strategy": release.strategy,
                "created_at": release.created_at,
                "deployed_at": release.deployed_at,
                "completed_at": release.completed_at
            },
            "steps": [
                {
                    "id": step.id,
                    "name": step.step_name,
                    "status": step.status,
                    "started_at": step.started_at,
                    "completed_at": step.completed_at,
                    "error_message": step.error_message
                }
                for step in steps
            ],
            "health_checks": [
                {
                    "id": check.id,
                    "name": check.check_name,
                    "status": check.status,
                    "response_time": check.response_time,
                    "checked_at": check.checked_at
                }
                for check in health_checks
            ]
        }
    
    async def list_releases(
        self, 
        environment: Optional[Environment] = None,
        status: Optional[ReleaseStatus] = None,
        limit: int = 50,
        offset: int = 0
    ) -> List[Release]:
        """获取发布列表"""
        query = self.db.query(Release)
        
        if environment:
            query = query.filter(Release.environment == environment)
        
        if status:
            query = query.filter(Release.status == status)
        
        return query.order_by(desc(Release.created_at)).offset(offset).limit(limit).all()
    
    async def _create_deployment_steps(self, release: Release):
        """创建部署步骤"""
        strategy_steps = self._get_strategy_steps(release.strategy)
        
        for order, step_config in enumerate(strategy_steps, 1):
            step = ReleaseStep(
                release_id=release.id,
                step_name=step_config["name"],
                step_order=order,
                description=step_config["description"],
                command=step_config.get("command"),
                config=step_config.get("config", {}),
                timeout=step_config.get("timeout", 300)
            )
            self.db.add(step)
        
        self.db.commit()
    
    async def _execute_deployment_strategy(self, release: Release) -> Dict[str, Any]:
        """执行部署策略"""
        try:
            if release.strategy == DeploymentStrategy.BLUE_GREEN:
                strategy = BlueGreenDeployment(release, self.db)
            elif release.strategy == DeploymentStrategy.CANARY:
                strategy = CanaryDeployment(release, self.db)
            elif release.strategy == DeploymentStrategy.ROLLING:
                strategy = RollingDeployment(release, self.db)
            else:
                raise ValueError(f"不支持的部署策略: {release.strategy}")
            
            # 执行部署
            result = await strategy.deploy()
            
            # 执行健康检查
            if result["success"]:
                health_result = await self.health_check_service.perform_health_checks(release.id)
                result["health_checks"] = health_result
            
            return result
            
        except Exception as e:
            logger.error(f"部署策略执行失败: {e}")
            return {"success": False, "error": str(e)}
    
    async def _execute_rollback(self, release: Release, target_release: Release) -> Dict[str, Any]:
        """执行回滚"""
        try:
            # 备份当前数据
            backup_result = await self.backup_service.create_backup(release)
            
            # 执行回滚步骤
            rollback_steps = [
                {"name": "停止当前服务", "command": "docker stop current-service"},
                {"name": "恢复目标版本", "command": f"docker run {target_release.version}"},
                {"name": "启动服务", "command": "docker start target-service"},
                {"name": "健康检查", "command": "health-check"}
            ]
            
            for step in rollback_steps:
                # 这里应该执行实际的回滚命令
                await asyncio.sleep(2)  # 模拟执行时间
            
            # 验证回滚结果
            health_result = await self.health_check_service.perform_health_checks(release.id)
            
            return {
                "success": True,
                "backup_location": backup_result.get("location"),
                "health_checks": health_result
            }
            
        except Exception as e:
            logger.error(f"回滚执行失败: {e}")
            return {"success": False, "error": str(e)}
    
    def _get_previous_stable_release(self, current_release: Release) -> Optional[Release]:
        """获取上一个稳定版本"""
        return self.db.query(Release).filter(
            and_(
                Release.environment == current_release.environment,
                Release.status == ReleaseStatus.SUCCESS,
                Release.id != current_release.id
            )
        ).order_by(desc(Release.created_at)).first()
    
    def _should_auto_rollback(self, release: Release) -> bool:
        """判断是否应该自动回滚"""
        # 检查失败步骤数量
        failed_steps = self.db.query(ReleaseStep).filter(
            and_(
                ReleaseStep.release_id == release.id,
                ReleaseStep.status == ReleaseStatus.FAILED
            )
        ).count()
        
        # 检查健康检查失败
        failed_health_checks = self.db.query(HealthCheck).filter(
            and_(
                HealthCheck.release_id == release.id,
                HealthCheck.status == False
            )
        ).count()
        
        # 如果失败步骤超过50%或健康检查全部失败，则自动回滚
        total_steps = self.db.query(ReleaseStep).filter(
            ReleaseStep.release_id == release.id
        ).count()
        
        return (failed_steps / total_steps > 0.5) or failed_health_checks > 0
    
    def _validate_version(self, version: str) -> bool:
        """验证版本号格式"""
        if not version:
            return False
        
        # 简单的版本号格式验证 (例如: v1.2.3, 1.2.3, v1.2.3-beta)
        import re
        pattern = r'^v?\d+\.\d+\.\d+(-[a-zA-Z0-9]+)?$'
        return bool(re.match(pattern, version))
    
    def _get_strategy_steps(self, strategy: DeploymentStrategy) -> List[Dict[str, Any]]:
        """获取策略对应的步骤配置"""
        steps_map = {
            DeploymentStrategy.BLUE_GREEN: [
                {"name": "准备蓝绿环境", "description": "准备蓝绿部署环境"},
                {"name": "部署到绿环境", "description": "将新版本部署到绿环境"},
                {"name": "健康检查", "description": "检查绿环境服务状态"},
                {"name": "切换流量", "description": "将流量从蓝环境切换到绿环境"},
                {"name": "清理蓝环境", "description": "清理旧的蓝环境"}
            ],
            DeploymentStrategy.CANARY: [
                {"name": "准备金丝雀环境", "description": "准备金丝雀发布环境"},
                {"name": "部署金丝雀版本", "description": "部署新版本到金丝雀环境"},
                {"name": "健康检查", "description": "检查金丝雀环境状态"},
                {"name": "逐步扩大流量", "description": "逐步增加金丝雀版本的流量"},
                {"name": "全量发布", "description": "完成全量发布"}
            ],
            DeploymentStrategy.ROLLING: [
                {"name": "准备滚动更新", "description": "准备滚动更新环境"},
                {"name": "逐步更新实例", "description": "逐个更新服务实例"},
                {"name": "健康检查", "description": "检查更新后的实例状态"},
                {"name": "验证服务", "description": "验证服务功能正常"},
                {"name": "完成更新", "description": "完成所有实例更新"}
            ]
        }
        
        return steps_map.get(strategy, [])
